package com.cn.qtms.base.service.impl;

import com.cn.qtms.base.entity.BaseEntity;
import com.cn.qtms.base.service.BaseJpaService;
import com.cn.qtms.common.page.QueryCondition;
import com.cn.qtms.common.page.SpecificationUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @Name:BaseServiceImpl
 * @Description: 基础业务接口实现
 * @Author: Neil
 * @Date: 2019-08-21 16:06
 **/
@Slf4j
public abstract class BaseJpaServiceImpl<T extends BaseEntity, ID> implements BaseJpaService<T, ID> {

    private static final Map<Class<?>, ExampleMatcher> MODE_LDEFAULT_MATCHER = new HashMap<>();

    @Override
    public T save(T entity) {
        return getDao().save(entity);
    }

    @Override
    public Iterable<T> save(Iterable<T> entities) {
        return getDao().saveAll(entities);
    }

    @Override
    public void deleteById(ID id) {
        getDao().deleteById(id);
    }

    @Override
    public void delete(T entity) {
        getDao().delete(entity);
    }

    @Override
    public void deleteInBatch(Iterable<T> entities) {
        getDao().deleteAll(entities);
    }

    @Override
    public void deleteAll() {
        getDao().deleteAll();
    }

    /**
     * Optional Java新特性 可以用Optional里面的方法判断是否为空
     * isPresent();//判断是否为空
     * ifPresent(Consumer<? super T> consumer) ;//如果value不为null，则执行consumer式的函数，为null不做事
     */
    @Override
    public Optional<T> getById(ID id) {
        return getDao().findById(id);
    }

    @Override
    public boolean exists(ID id) {
        return getDao().existsById(id);
    }

    @Override
    public boolean exists(T example) {
        ExampleMatcher match = getDefaultMatch(example.getClass());
        Example<T> of = Example.of(example, match);
        return exists(of);
    }

    private ExampleMatcher getDefaultMatch(Class<? extends Object> clazz) {
        if (!MODE_LDEFAULT_MATCHER.containsKey(clazz)) {
            MODE_LDEFAULT_MATCHER.put(clazz, genDefaultMatcher(clazz));
        }
        return MODE_LDEFAULT_MATCHER.get(clazz);
    }

    private ExampleMatcher genDefaultMatcher(Class<? extends java.lang.Object> class1) {
        Field[] declaredFields = class1.getDeclaredFields();
        ExampleMatcher match = ExampleMatcher.matching().withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
        for (Field field : declaredFields) {
            StringMatcherType annotation = field.getAnnotation(StringMatcherType.class);
            if (annotation != null) {
                match = match.withMatcher(field.getName(), ExampleMatcher.GenericPropertyMatcher.of(annotation.value()));
            }
        }

        return match;
    }

    @Override
    public boolean exists(Example<T> example) {
        return getDao().exists(example);
    }

    @Override
    public long count() {
        return getDao().count();
    }

    @Override
    public long count(T example) {
        ExampleMatcher match = getDefaultMatch(example.getClass());
        Example<T> of = Example.of(example, match);
        return count(of);
    }

    @Override
    public <S extends T> long count(Example<S> example) {
        return getDao().count(example);
    }

    @Override
    public List<T> findAll() {
        return getDao().findAll();
    }

    @Override
    public List<T> findAll(Sort sort) {
        return getDao().findAll(sort);
    }

    @Override
    public List<T> findAll(Iterable<ID> ids) {
        return getDao().findAllById(ids);
    }

    @Override
    public List<T> findAll(T example) {
        ExampleMatcher match = getDefaultMatch(example.getClass());
        Example<T> of = Example.of(example, match);
        return findAll(of);
    }

    @Override
    public <S extends T> List<S> findAll(Example<S> example) {
        return getDao().findAll(example);
    }

    @Override
    public <S extends T> List<S> findAll(S example, Sort sort) {
        ExampleMatcher match = getDefaultMatch(example.getClass());
        Example<S> of = Example.of(example, match);
        return findAll(of, sort);
    }

    @Override
    public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
        return getDao().findAll(example, sort);
    }

    @Override
    public Page<T> findAll(Pageable pageable) {
        return getDao().findAll(pageable);
    }

    @Override
    public <S extends T> Page<S> findAll(S example, Pageable pageable) {
        ExampleMatcher match = getDefaultMatch(example.getClass());
        Example<S> of = Example.of(example, match);
        return findAll(of, pageable);
    }

    @Override
    public Page<T> findAll(T example, List<QueryCondition> conditions, Pageable pageable) {
        if (conditions == null || conditions.size() == 0) {
            return findAll(example, pageable);
        }

        Specification<T> c2s = SpecificationUtil.c2s(conditions);

        ExampleMatcher match = getDefaultMatch(example.getClass());
        Example<T> of = Example.of(example, match);
        Specification<T> result = new Specification<T>() {

            /**
             *
             */
            private static final long serialVersionUID = 1L;

            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Predicate predicate = QueryByExamplePredicateBuilder.getPredicate(root, cb, of);
                Predicate and = cb.and(predicate, c2s.toPredicate(root, query, cb)); // predicate放在前面，可能出现的1=1在前面
                return and;
            }

        };
        return getDao().findAll(result, pageable);
    }

    @Override
    public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
        return getDao().findAll(example, pageable);
    }

    @Override
    public boolean exists(List<QueryCondition> conditions) {
        return count(conditions) > 0;
    }

    @Override
    public long count(List<QueryCondition> conditions) {
        Specification<T> c2s = SpecificationUtil.c2s(conditions);

        return getDao().count(c2s);
    }

    @Override
    public List<T> findAll(List<QueryCondition> conditions) {
        Specification<T> c2s = SpecificationUtil.c2s(conditions);

        return getDao().findAll(c2s);
    }

    @Override
    public List<T> findAll(List<QueryCondition> conditions, Sort sort) {
        Specification<T> c2s = SpecificationUtil.c2s(conditions);

        return getDao().findAll(c2s, sort);
    }

    @Override
    public Page<T> findAll(List<QueryCondition> conditions, Pageable pageable) {
        Specification<T> c2s = SpecificationUtil.c2s(conditions);

        return getDao().findAll(c2s, pageable);
    }


}